/*
 * Author: Yuan Li
 * Title: 15123 Summer session lab2
 * Date: Aug 9 2011
 */



/*	--------------------------------------------------------------------------------------------------
	lab-02 Starter Code By Kun Qian (edited by hoffman 9/6/'09) changed 50000 to 30000
*/


#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h> /* toupper() function and others */

#define MAX_WORDLEN 50
#define MAX_WORDS   30000 /* Don't change this or you might overflow your allotted stack space. */
#define true 1
#define false 0

/*UNIT test switch */
#define UNIT false


/* Use the following Macros to print EVERYTHING while executing the menu function */

/* Menu print */
#define printMenu() printf("\nChoose: 'P'rint, 'S'earch, 'I'nsert, 'R'emove, 'C'ount, 'Q'uit :")
#define optP() printf("You chose to 'P'rint:\n")
#define optS() printf("Enter word to 'S'earch:\n")
#define optI() printf("Enter word to 'I'nsert:\n")
#define optR() printf("Enter word to 'R'emove:\n")
#define optC() printf("You chose to 'C'ount:\n")
#define optQ() printf("You chose to 'Q'uit:\n")

/* Insert print */
#define printInserted(word) printf("%s was successfully inserted.\n", word)
#define printFailedToInsert(word) printf("Duplicate found, %s was not inserted.\n", word)

/* Remove print */
#define printRemove(word) printf("%s was successfully removed.\n", word)
#define printFailedToRemove(word) printf("%s cannot be found, and was not removed.\n", word)

/* Search print */
#define printFound(word) printf("%s was successfully found.\n", word)
#define printNotFound(word) printf("%s was not found.\n", word)

/* Count print */
#define printCount(count) printf("%d words currently in the dictionary.\n", count)

/*  Some of my small helper function: */

/*
 * searchDuplicate -- find out whether the "word" is in the current dictionary
 * @param dictionary[][MAX_WORDLEN] -- the address of the dictionary 2D array
 * @param cnt -- indicate numbers of words in the dictionary
 * @param word -- the word need to find 
 * @return -- will return true if the word is found, otherwise return false
 */

int searchDuplicate( char dictionary[][MAX_WORDLEN], int cnt, char *word)
{
	int row;
	/* if the dictionary is empty, there will be no duplicate. */
	if ( cnt == 0){
	   return false;
  	}

	/* compare the input word with the word in the dictionary */
	for (row=0; row < cnt; row++){
		/* strcmp will return difference or 0 */
		if (!strcmp(dictionary[row], word)){
			   return true;
		}
 	}

	/* no same word? return false */
	return false;
}

/*
 * removeNewln -- replace the first '\0' to the '\0'
 * @param wordBuffer -- address of wordBuffer
 * @param length -- size of the wordBuffer
 */
void removeNewln(char wordBuffer[], int length){
	int index;
	for (index=0; index < length; index++){
		if (wordBuffer[index] == '\n'){
			wordBuffer[index] = '\0';
			return;
		}	
	}	

	/* no '\n' ??, problely get wrong */
	fprintf(stdout, "no \"newline\" found in the wordBuffer!\n");
	return;
}

/* 
 * readword -- will read a word from the keyboard
 * @param wordBuffer -- address of the wordBuffer
 * @param length of the wordBuffer
 */
void readWord(char wordBuffer[], int length)
{
	void* ret;
	ret = fgets(wordBuffer, length, stdin);
	removeNewln(wordBuffer, length);
	if (ret == 0){
		printf("Error happened at readWord!!\n");
		exit(EXIT_FAILURE);
	}
}
/**************************************/

/* From the menu this gets called everytime the user selects 'P'rint. In this case you just pass in stdout
    for the stream arg.  If the user type 'Q'uit then you must first open up the output file and pass in the
    file handle.  Be sure to close that file as soon as this function is done. */


/* printDictionary -- print out the 2D dictionary to a specific stream
 * @param dictionary -- the address of the 2D dictionary
 * @param cnt -- the variable which indicates the how many words stored in the dictionary
 * @param stream -- the stream need to print to
 */
void printDictionary( char dictionary[][MAX_WORDLEN], int cnt, FILE *stream )
{	int row;
	int wordcnt = 0;
	for (row=0; row < cnt; row++){
		/* if it reaches the line length limitation */
		if ((wordcnt+strlen(dictionary[row])) > 80){
			fprintf(stream, "\n");
			/* clear wordcnt for a newline*/
			wordcnt = 0;
		}
		fprintf(stream, "%s ", dictionary[row]);
		/* accumulate wordcnt plus space */
		wordcnt = wordcnt + strlen(dictionary[row]) + 1;
	}
	
	return;
}

/*
 * searchWord -- will return true if the given is found in the dictionary, vice versa 
 * @param dictionary -- address of the 2D dictionary array
 * @param count -- address of variable which keep track of numbers of word in the dictionary
 * @param word -- a given word need to find out
 */ 
void searchWord(char dictionary[][MAX_WORDLEN], int count, char *word)
{
	if (searchDuplicate(dictionary, count, word) == true){
		printFound(word);
		return;
	}
	else{
		printNotFound(word);
		return;
	}	
}

/*
 * insertWord -- insert a new word into the dictionary
 * @param dictionary[][MAX_WORDLEN] -- the address of the dictionary 2D array
 * @param count -- the address of variable which indicates numbers of words in the dictionary
 * @param word -- the word need to insert
 */

void insertWord( char dictionary[][MAX_WORDLEN], int *count, char *word, int verbose)
{
	int row;
	/* maybe need steps to exam the input whether the args is valid address before use them*/

	/* check the length of the word, we have max_wordlength limitation */
	if (strlen(word) > MAX_WORDLEN){
		fprintf(stdout,"The word has exceed the word length limitation MAX_WORDLEN!!\n");
		return;
	}	   

	/* check whether the dictionary is full */
	if (*count >= MAX_WORDS){
		fprintf(stdout, "The dictionary is full, cannot insert any word!!\n");
		return;
	}

	/* we should not generate duplicate word in the dictionary */
	if (searchDuplicate(dictionary, *count, word)){
		if (verbose)
			printFailedToInsert(word);
		return;
	}

	/* if the dictionary is empty, just insert this word */
	if (*count == 0){
		strcpy(dictionary[0],word);
		(*count)++;
		if (verbose)
			printInserted(word);
		return;
	}	
	

	/* insert the new into the proper position in the dictionary */
	for (row= (*count)-1; row >= 0; row--){
		/* if new word bigger than last word in the dictionary, just insert new word after it */
		if (strcmp(word, dictionary[row]) > 0){
			strcpy(dictionary[row+1], word);
			(*count)++;
			if (verbose)
				printInserted(word);
			return;
		}
		/* elsewise, push the last word to the rear */
		else{
			strcpy(dictionary[row+1],dictionary[row]);
		}	
	}
	/* no exist word bigger than new word, just insert it at the first place of the dictionary */
	strcpy(dictionary[0],word);
	(*count)++;
	if (verbose)
		printInserted(word);
	return;

	/* should not get here */
	fprintf(stdout,"Error happened at insertWord!\n");
}

/*
 * removeWord -- will remove a given a word from the dictionary, then shift up all the words
 * 				 behind it a position
 * 	@param dictionary -- address of the 2D array dictionary
 * 	@param count -- address of variable which keep track of number of words in the dictionary
 * 	@param word -- the word need to be removed
 */
void removeWord( char dictionary[][MAX_WORDLEN], int *count, char *word )
{
	int row;
	int foundFlag = false;
	/* check the length of the word, we have max_wordlength limitation */
	if (strlen(word) > MAX_WORDLEN){
		fprintf(stdout,"The word has exceed the word length limitation MAX_WORDLEN!!\n");
		return;
	}	

	/* find the word then shift the behind words */
	for (row=0; row < *count; row++){

		if (foundFlag == true){
			strcpy(dictionary[row-1], dictionary[row]);
		}	

		if (strcmp(dictionary[row],word) == 0){
			foundFlag = true;
			printRemove(word);
		}
	}
	
	/* the given word is not found in the dictionary */
	if (foundFlag == false){
		printNotFound(word);
		return;
	}	

	/* update the count before return */
	(*count)--;
	return;
}

/*
 * countWord -- will display how many words stored in the dictionary
 * @param count -- address of variable which keep track of number of words in the dictionary
 */
void countWord(int count)
{
	printCount(count);
	return;
}

/*
 * quit -- will print out the dictionary to the specific file, then quit the program
 * @param dictionary -- address of 2D dictionary array
 * @param count -- address of variable which keep track of number of words in the dictionary
 * @param outfileName -- name of output file
 */
void quit(char dictionary[][MAX_WORDLEN], int count, char *outfileName )
{
	FILE* output;

	/* open the file and check the failure */
	output = fopen(outfileName, "wt");
	if (output == NULL){
		printf("fail to open the output file %s\n", outfileName);
		exit(EXIT_FAILURE);
	}

	printDictionary(dictionary, count, output);
	fclose(output);
	return;
}


/* LOADDICTIONARY
 * loadDictionary -- will load a banch of words from a file to a 2D dictionary array in order
 * @param infilname -- the file which read from
 * @param dictionary -- 2D dictionary array
 * @param count -- address of the variable which keep track of numbers words in the dictionary
 */
void loadDictionary( char *infilname, char dictionary[][MAX_WORDLEN], int *count )
{
	FILE* infile;
	char wordBuffer[MAX_WORDLEN] = {'\0'};
	
	/* open the file, fopen will return a pointer for that file, return NULL if fails to open */
	infile = fopen(infilname, "rt");
	if (infile == NULL){
		fprintf(stderr,"fail to open file %s\n!",infilname);
		exit(EXIT_FAILURE);
	}

	while(fgets(wordBuffer, MAX_WORDLEN, infile) != NULL){
		removeNewln(wordBuffer, MAX_WORDLEN);
		insertWord(dictionary, count, wordBuffer, false);
	}

	/* close the file before return */
	fclose(infile);
	return;
}


/* Other than the printf calls above in the macros, and the ones we put into main,
	you should not call printf anywhere else.

	You DO need fprintf in your printDictionary
*/

/*
 * doMenu -- is infinite loop which waits for the user's input, then gives response
 * @param outfileName -- the name of the file to output
 * @param dictionary -- address of the 2D dictionary array
 * @param count -- address of variable which keeps track of number of word in the dictionary
 */
void doMenu( char *outfileName, char dictionary[MAX_WORDS][MAX_WORDLEN], int *count)
{
	char ch;
	char wordBuffer[MAX_WORDLEN] = {'\0'};
	while (true){
		printMenu();
		/* reader a charactor(choice) from keyboard 
		 * the space before %c will eat up the space
		 * %* will eat up the all reminder charactor
		 * [^\n] will stop the scan when encounter a newline
		 * getchar() will eat the newline
		 */
		if (scanf(" %c%*[^\n]", &ch) == 0){
			printf("Error happened at scanf!\n");
			exit(EXIT_FAILURE);
		}	
		getchar();

		/* choice handle */
		if (ch == 'P'){
			optP();
			printDictionary(dictionary, *count, stdout);
			printf("\n");
		}
		else if (ch == 'S'){
			optS();
			readWord(wordBuffer, MAX_WORDLEN);
			searchWord(dictionary, *count, wordBuffer);
		}
		else if (ch == 'I'){
			optI();
			readWord(wordBuffer, MAX_WORDLEN);
			insertWord(dictionary, count, wordBuffer, true);
		}	
		else if (ch == 'R'){
			optR();
			readWord(wordBuffer, MAX_WORDLEN);
			removeWord(dictionary, count, wordBuffer);
		}
		else if (ch == 'C'){
			optC();
			countWord(*count);
		}
		else if (ch == 'Q'){
			quit(dictionary, *count, outfileName);
			return;
		}
		else{
			printf("unknow choice!!\n");
		}	

	}	
}
	
/*    M A I N   F U N C T I O N  */
#if UNIT == false
int main( int argc, char *argv[] )
{
	time_t startTime, stopTime;
	clock_t startCPU, stopCPU;
	double elapsedCPUsecs;


	char dictionary[MAX_WORDS][MAX_WORDLEN];
	int count=0;

	if (argc < 3 )
	{
		printf("usage: ./%s <infileName> <outFilename>\n",argv[0]);
		return EXIT_FAILURE;
	}

	startTime = time( NULL );
	printf("\nStarting load of %s at %s", argv[1], ctime( &startTime) );

	startCPU = clock();
	loadDictionary( argv[1], dictionary, &count);
	stopCPU = clock();
	stopTime = time( NULL );
	printf("Finished load of %s at %s", argv[1], ctime( &stopTime) );
	elapsedCPUsecs = ((double)(stopCPU-startCPU)) / CLOCKS_PER_SEC;
	printf("Elapsed CPU seconds: %f\n",  elapsedCPUsecs );

	doMenu( argv[2],  dictionary, &count);
	return EXIT_SUCCESS; /* EXIT_SUCCESS defined as 0 in stdlib.h */
}
#endif



/* unit test */


#if UNIT == true
int main(int argc, char **argv)
{
	int count=0;	
	char dictionary[MAX_WORDS][MAX_WORDLEN];

	/* test case for printDictionary and searchDuplicate function
	printf("for a empty dic to call searchDuplicate:1 for true, 0 for false\n%d\n",\
			searchDuplicate(dictionary, count, "hello"));
	strcpy(dictionary[0], "hello");
	strcpy(dictionary[1], "world");
	strcpy(dictionary[2], "test");
	strcpy(dictionary[3], "apple");
	count = 4;
	printDictionary(dictionary,count,stdout);
	printf("search \"test\":%d\n",searchDuplicate(dictionary, count, "test"));
	printf("search \"app\":%d\n",searchDuplicate(dictionary, count, "app"));
	*/

	/* test loadDictionary */	
	loadDictionary(argv[1], dictionary, &count);
   	printDictionary(dictionary, count, stdout);	
	printf("%d\n",count);

	/* test removeWord 
	removeWord(dictionary, &count, "waits");
	printf("%d\n",count);
	removeWord(dictionary, &count, "lenove");
	printf("%d\n",count);
   	printDictionary(dictionary, count, stdout);	
	*/
	return 0;
}	

#endif


